//
//  CTBaseCycleScrollView.swift
//  CTPhotoBrowser
//
//  Created by Andney Jack on 2021/2/25.
//

import UIKit

public protocol CTCycleScrollItemViewProtocol {
    /// 当前Item是否处于显示状态
    var isShowed: Bool { get set }
    /// 是否已成功获取到图片
    var didFetchImage: Bool { get set }
    /// 当前显示的图片
    var currentImage: UIImage? { get }
}

/// 轮播图基类
open class CTBaseCycleScrollView<T: UIView>: UIView, UIScrollViewDelegate where T: CTCycleScrollItemViewProtocol {
    public private(set) var bgScrollView: UIScrollView?
    public private(set) var previousView: T?
    public private(set) var currentView: T?
    public private(set) var nextView: T?

    /// item之间的间距，默认为10
    public let itemSpacing: CGFloat

    /// 是否自动轮播
    public var autoScroll: Bool {
        get {
            return m_autoScroll
        }
        set {
            m_autoScroll = newValue
            if newValue {
                startTimer()
            } else {
                stopTimer()
            }
        }
    }

    /// 自动轮播的时间间隔
    public var autoScrollTimeInterval: TimeInterval = 3.5
    /// 切换动画的时长
    public var animatedDuration: TimeInterval = 0.35
    /// 图片对象列表，也可以是自定义对象
    public var imageObjList: [Any]?
    /// 加载图片的回调
    public var imageLoadHandler: ((_ itemView: T, _ imgObj: Any?, _ index: Int, _ placeholder: UIImage?) -> Void)?
    /// 占位图
    public var placeholderImage: UIImage?

    /// 当前索引
    public var currentIndex: Int {
        if imageObjList == nil {
            return 0
        }
        return m_currentIdx
    }

    /// 前一个索引
    public var previousIndex: Int {
        guard let imgList = imageObjList else { return 0 }
        let index = m_currentIdx - 1
        return index < 0 ? imgList.count - 1 : index
    }

    /// 下一个索引
    public var nextIndex: Int {
        guard let imgList = imageObjList else { return 0 }
        let index = m_currentIdx + 1
        return index >= imgList.count ? 0 : index
    }

    private var m_autoScroll: Bool = true
    private var m_currentIdx: Int = 0
    private weak var scrollTimer: Timer? // 自动轮播定时器

    public required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override init(frame: CGRect) {
        itemSpacing = 10
        super.init(frame: frame)
        m_loadUIIfNeeded()
    }

    public init(frame: CGRect, itemSpacing: CGFloat, itemBuilder: (() -> T)? = nil) {
        self.itemSpacing = itemSpacing
        super.init(frame: frame)
        m_loadUIIfNeeded(itemBuilder: itemBuilder)
    }

    // 初始化UI控件
    private func m_loadUIIfNeeded(itemBuilder: (() -> T)? = nil) {
        if bgScrollView != nil {
            return
        }

        layer.masksToBounds = true

        bgScrollView = UIScrollView()
        bgScrollView?.showsVerticalScrollIndicator = false
        bgScrollView?.showsHorizontalScrollIndicator = false
        if #available(iOS 11.0, *) {
            bgScrollView?.contentInsetAdjustmentBehavior = .never
        }
        bgScrollView?.delegate = self
        bgScrollView?.bounces = false
        bgScrollView?.isPagingEnabled = true
        bgScrollView?.isScrollEnabled = false
        addSubview(bgScrollView!)

        // 初始化三个子视图
        if let builder = itemBuilder {
            previousView = builder()
            currentView = builder()
            nextView = builder()
        } else {
            previousView = getItemView()
            currentView = getItemView()
            nextView = getItemView()
        }

        bgScrollView?.addSubview(previousView!)
        bgScrollView?.addSubview(currentView!)
        bgScrollView?.addSubview(nextView!)

        // 添加约束
        bgScrollView?.translatesAutoresizingMaskIntoConstraints = false
        previousView?.translatesAutoresizingMaskIntoConstraints = false
        currentView?.translatesAutoresizingMaskIntoConstraints = false
        nextView?.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint(item: bgScrollView!, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: bgScrollView!, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: bgScrollView!, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: bgScrollView!, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: bgScrollView!, attribute: .width, relatedBy: .equal, toItem: self, attribute: .width, multiplier: 1, constant: itemSpacing).isActive = true

        NSLayoutConstraint(item: previousView!, attribute: .left, relatedBy: .equal, toItem: bgScrollView, attribute: .left, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: previousView!, attribute: .top, relatedBy: .equal, toItem: bgScrollView, attribute: .top, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: previousView!, attribute: .height, relatedBy: .equal, toItem: bgScrollView, attribute: .height, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: previousView!, attribute: .width, relatedBy: .equal, toItem: bgScrollView, attribute: .width, multiplier: 1, constant: -itemSpacing).isActive = true

        NSLayoutConstraint(item: currentView!, attribute: .top, relatedBy: .equal, toItem: previousView, attribute: .top, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: currentView!, attribute: .bottom, relatedBy: .equal, toItem: previousView, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: currentView!, attribute: .width, relatedBy: .equal, toItem: previousView, attribute: .width, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: currentView!, attribute: .left, relatedBy: .equal, toItem: previousView, attribute: .right, multiplier: 1, constant: itemSpacing).isActive = true

        NSLayoutConstraint(item: nextView!, attribute: .top, relatedBy: .equal, toItem: previousView, attribute: .top, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: nextView!, attribute: .bottom, relatedBy: .equal, toItem: previousView, attribute: .bottom, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: nextView!, attribute: .width, relatedBy: .equal, toItem: previousView, attribute: .width, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: nextView!, attribute: .left, relatedBy: .equal, toItem: currentView, attribute: .right, multiplier: 1, constant: itemSpacing).isActive = true
        NSLayoutConstraint(item: nextView!, attribute: .right, relatedBy: .equal, toItem: bgScrollView, attribute: .right, multiplier: 1, constant: -itemSpacing).isActive = true
    }

    // 重新设置偏移量
    override open func layoutSubviews() {
        super.layoutSubviews()

        guard let scrollView = bgScrollView else { return }
        guard scrollView.bounds.width > 0, scrollView.bounds.height > 0 else {
            return
        }

        scrollView.contentOffset = CGPoint(x: scrollView.bounds.width, y: 0)
    }

    // 解决当父View释放时，当前视图因为被Timer强引用而不能释放的问题
    override open func willMove(toSuperview newSuperview: UIView?) {
        if newSuperview == nil {
            stopTimer()
        }
    }

    // MARK: - ———————— UIScrollViewDelegate

    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
    }

    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        stopTimer()
    }

    public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
    }

    public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if !decelerate {
            m_scrollViewDidEndAnimation(scrollView)
            startTimer()
        }
    }

    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        m_scrollViewDidEndAnimation(scrollView)
        startTimer()
    }

    // 动画结束
    private func m_scrollViewDidEndAnimation(_ scrollView: UIScrollView) {
        let index = Int((scrollView.contentOffset.x + itemSpacing) / scrollView.bounds.width)
        if index == 1 {
            return
        }

        if index == 0 { // 查看上一张
            m_currentIdx = previousIndex
        } else if index == 2 { // 查看下一张
            m_currentIdx = nextIndex
        }

        scrollView.contentOffset = CGPoint(x: scrollView.frame.width, y: 0)

        refreshPhotoViewImages()
    }

    // MARK: - ———————— Public Method

    /// 获取子视图，子类可以重写
    public func getItemView() -> T {
        return T()
    }

    /// 设置图片数据
    public func updateImageList(_ imgList: [Any]) {
        imageObjList = imgList
        m_currentIdx = 0
        refreshPhotoViewImages()

        if imgList.count > 1 {
            bgScrollView?.isScrollEnabled = true
        } else {
            bgScrollView?.isScrollEnabled = false
        }

        if autoScroll {
            startTimer()
        }
    }

    /// 开启定时器
    public func startTimer() {
        stopTimer()

        if autoScrollTimeInterval <= 0 || autoScroll == false {
            return
        }

        if (imageObjList?.count ?? 0) < 2 {
            return
        }

        let timer = Timer.scheduledTimer(timeInterval: autoScrollTimeInterval, target: self, selector: #selector(autoScrollToNextItem(_:)), userInfo: nil, repeats: true)
        RunLoop.main.add(timer, forMode: .common)
        scrollTimer = timer
    }

    /// 关闭定时器
    public func stopTimer() {
        guard scrollTimer != nil else {
            return
        }

        scrollTimer?.invalidate()
        scrollTimer = nil
    }

    /// 展示上一个
    public func goBack(animated: Bool) {
        guard let _ = bgScrollView else { return }

        m_navigationToItem(offsetX: 0, animated: animated, manually: true)
    }

    /// 展示下一个
    public func goForward(animated: Bool) {
        guard let scrollView = bgScrollView else { return }

        m_navigationToItem(offsetX: 2.0 * scrollView.bounds.width, animated: animated, manually: true)
    }

    /// 重新布局图片
    public func refreshPhotoViewImages() {
        previousView?.isShowed = false
        currentView?.isShowed = true
        nextView?.isShowed = false

        guard let imgList = imageObjList, imgList.count > 0 else {
            if let handler = imageLoadHandler, let itemView = currentView {
                handler(itemView, nil, currentIndex, placeholderImage)
            }
            return
        }

        m_setItemViewImage(index: currentIndex, itemView: currentView)
        m_setItemViewImage(index: previousIndex, itemView: previousView)
        m_setItemViewImage(index: nextIndex, itemView: nextView)
    }

    // MARK: - ———————— Private Method

    /// 定时器引起的自动切换到下一个
    @objc private func autoScrollToNextItem(_ timer: Timer) {
        guard let scrollView = bgScrollView else { return }

        m_navigationToItem(offsetX: 2.0 * scrollView.bounds.width, animated: true, manually: true)
    }

    /// 切换上一个、下一个
    /// - Parameters:
    ///   - offsetX: 偏移量
    ///   - animated: 是否包含动画
    ///   - manually: 是否为外部手动调用
    private func m_navigationToItem(offsetX: CGFloat, animated: Bool, manually: Bool) {
        guard let scrollView = bgScrollView else { return }

        if manually {
            stopTimer()
        }

        if animated {
            UIView.animate(withDuration: animatedDuration, delay: 0.0, options: [.beginFromCurrentState, .curveEaseInOut]) { [weak self] in
                guard let scrollView = self?.bgScrollView else { return }

                scrollView.setContentOffset(CGPoint(x: offsetX, y: 0), animated: false)
                scrollView.isUserInteractionEnabled = false

            } completion: { [weak self] _ in
                guard let scrollView = self?.bgScrollView else { return }

                self?.m_scrollViewDidEndAnimation(scrollView)
                scrollView.isUserInteractionEnabled = true

                if manually {
                    self?.startTimer()
                }
            }
        } else {
            scrollView.setContentOffset(CGPoint(x: offsetX, y: 0), animated: false)
            m_scrollViewDidEndAnimation(scrollView)

            if manually {
                startTimer()
            }
        }
    }

    // 设置图片
    private func m_setItemViewImage(index: Int, itemView: T?) {
        guard let view = itemView, let handler = imageLoadHandler else { return }

        guard let imgList = imageObjList, imgList.count > 0 else {
            handler(view, nil, index, placeholderImage)
            return
        }

        if index >= 0 && index < imgList.count {
            handler(view, imgList[index], index, placeholderImage)
        } else {
            handler(view, nil, index, placeholderImage)
        }
    }
}
